﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using CellarDoor.Gui.Animation;

namespace CellarDoor.Gui.Elements
{
    public partial class UiElement
    {
        /// <summary>
        /// Occurs when mouse moves inside the area of this item.
        /// </summary>
        public event UiEventHandler MouseMoveInside;

        /// <summary>
        /// Occurs when mouse is moved outside and component is focused (Clicked)
        /// </summary>
        public event UiEventHandler MouseMoveOutside;

        /// <summary>
        /// Occurs when mouse enters the area of this item.
        /// </summary>
        public event UiEventHandler MouseEnter;

        /// <summary>
        /// Occurs when mouse leaves the area of this item.
        /// </summary>
        public event UiEventHandler MouseExit;

        /// <summary>
        /// Occurs when left mousebutton was pressed over the area of this item.
        /// </summary>
        public event UiEventHandler MousePress;

        /// <summary>
        /// Occurs when item was clicked.
        /// </summary>
        public event UiEventHandler Click;

        /// <summary>
        /// Occurs when component is draged.
        /// </summary>
        public event UiEventHandler MouseDrag;

        /// <summary>
        /// Occurs when a manager was set or changed on this element.
        /// </summary>
        public event UiEventHandler ManagerChanged;

        /// <summary>
        /// Occurs when the position or size of this element have changed
        /// </summary>
        public event UiEventHandler BoundsChanged;

        /// <summary>
        /// Occurs when the componentstate changed.
        /// </summary>
        public event UiEventHandler StateChanged;

        private List<UiElement> elementsToUpdate = new List<UiElement>();

        public virtual void UpdateState(GameTime gameTime, UiMouseProxy mouse)
        {
            if (!Enabled)
            {
                Style.State = UiVisualState.Disabled;
                return;
            }

            bool newInside = Layout.AreaScaled.Contains(mouse.PositionNew);
            bool oldInside = Layout.AreaScaled.Contains(mouse.PositionOld);

            if (!lastFrameFocused && Focused)
            {
                // on got focus
            }
            if (lastFrameFocused && !Focused)
            {
                // on lost focus
            }
            lastFrameFocused = Focused;

            if (Dragable && Focused && mouse.ButtonLeftPressed)
            {
                OnMouseDrag(mouse);
            }

            if (newInside)
            {
                if (!oldInside)
                {
                    // ------------------------
                    // New inside Old outinside
                    // ------------------------
                    OnMouseEnter(mouse);
                }
                else
                {
                    // ------------------
                    // New and Old inside
                    // ------------------
                    if (Focused)
                    {
                        if (mouse.ButtonLeftJustPressed)
                        {
                            OnMousePress(mouse);
                            Style.State = UiVisualState.Down;
                        }
                        else if (mouse.ButtonLeftPressed)
                        {
                            if (mouse.PositionDelta != Point.Zero)
                            {
                                OnMouseMoveInside(mouse);
                            }
                            Style.State = UiVisualState.Down;
                        }
                        else if (mouse.ButtonLeftJustReleased)
                        {
                            OnMouseClick(mouse);
                            Style.State = UiVisualState.Up;
                        }
                        else
                        {
                            Style.State = UiVisualState.Up;
                        }
                    }
                }
            }
            else
            {
                if (mouse.ButtonLeftJustReleased)
                {
                    Style.State = UiVisualState.Default;
                }

                if (oldInside)
                {
                    // --------------------------
                    // New outside and Old inside
                    // --------------------------
                    OnMouseExit(mouse);
                    if (!Focused || !mouse.ButtonLeftPressed)
                    {
                        Style.State = UiVisualState.Default;
                    }
                }
                else
                {
                    // ---------------------
                    // New and Old outside
                    // ---------------------
                    if (Focused && mouse.ButtonLeftPressed &&
                        mouse.PositionDelta != Point.Zero)
                    {
                        OnMouseMoveOutside(mouse);
                    }
                }
            }

            if (Style.State == UiVisualState.Up && !newInside)
            {
                Style.State = UiVisualState.Default;
            }

            elementsToUpdate.Clear();
            elementsToUpdate.AddRange(elements);
            foreach (UiElement c in elementsToUpdate)
            {
                c.UpdateState(gameTime, mouse);
            }
        }

        protected virtual void OnMouseMoveOutside(UiMouseProxy mouse)
        {
            OnMouseMoveOutside(new UiMouseArgs(mouse, this));
        }

        private void OnMouseMoveOutside(UiMouseArgs arg)
        {
            if (MouseMoveOutside != null)
            {
                MouseMoveOutside(this, arg);
            }

            if (Parent != null && !arg.Handeled)
            {
                Parent.OnMouseMoveOutside(arg);
            }
        }

        protected virtual void OnMouseExit(UiMouseProxy mouse)
        {
            OnMouseExit(new UiMouseArgs(mouse, this));
        }

        private void OnMouseExit(UiMouseArgs arg)
        {
            if (MouseExit != null)
            {
                MouseExit(this, arg);
            }

            if (Parent != null && !arg.Handeled)
            {
                Parent.OnMouseExit(arg);
            }
        }

        protected virtual void OnMouseEnter(UiMouseProxy mouse)
        {
            Style.State = mouse.ButtonLeftPressed ? UiVisualState.Down : UiVisualState.Up;

            OnMouseEnter(new UiMouseArgs(mouse, this));
        }

        private void OnMouseEnter(UiMouseArgs arg)
        {
            if (MouseEnter != null)
            {
                this.MouseEnter(this, arg);
            }

            if (Parent != null && !arg.Handeled)
            {
                Parent.OnMouseEnter(arg);
            }
        }

        protected virtual void OnMouseClick(UiMouseProxy mouse)
        {
            OnMouseClick(new UiMouseArgs(mouse, this));
        }

        private void OnMouseClick(UiMouseArgs arg)
        {
            if (Click != null)
            {
                Click(this, arg);
            }

            if (Parent != null && !arg.Handeled)
            {
                Parent.OnMouseClick(arg);
            }
        }

        protected virtual void OnMousePress(UiMouseProxy mouse)
        {
            OnMousePress(new UiMouseArgs(mouse, this));
        }

        private void OnMousePress(UiMouseArgs arg)
        {
            if (MousePress != null)
            {
                MousePress(this, arg);
            }

            if (Parent != null && !arg.Handeled)
            {
                Parent.OnMousePress(arg);
            }
        }

        protected virtual void OnMouseMoveInside(UiMouseProxy mouse)
        {
            OnMouseMoveInside(new UiMouseArgs(mouse, this));
        }

        private void OnMouseMoveInside(UiMouseArgs arg)
        {
            if (MouseMoveInside != null)
            {
                MouseMoveInside(this, arg);
            }

            if (Parent != null && !arg.Handeled)
            {
                Parent.OnMouseMoveInside(arg);
            }
        }

        protected virtual void OnMouseDrag(UiMouseProxy mouse)
        {
            Vector2 newOffset = Layout.Location;
            Vector2 delta = new Vector2(mouse.PositionDelta.X, mouse.PositionDelta.Y) / Manager.ElementScale;

            if (DragFromCenter)
            {
                delta = new Vector2(mouse.PositionNew.X, mouse.PositionNew.Y) - (Layout.Position + Layout.Size / 2.0f);
            }

            if (DragableAlongX)
            {
                newOffset.X += delta.X;
            }
            if (DragableAlongY)
            {
                newOffset.Y += delta.Y;
            }

            if (DragLimitEnable && parent != null)
            {
                float padLeft = Math.Max(parent.Layout.Padding.Left, this.Layout.Margin.Left);
                float padRight = Math.Max(parent.Layout.Padding.Right, this.Layout.Margin.Right);
                float padTop = Math.Max(parent.Layout.Padding.Top, this.Layout.Margin.Top);
                float padBottom = Math.Max(parent.Layout.Padding.Bottom, this.Layout.Margin.Bottom);

                if (parent.Layout.Size.X - Layout.Size.X - padRight > 0)
                {
                    newOffset.X = MathHelper.Clamp(newOffset.X, padLeft, parent.Layout.Size.X - Layout.Size.X - padRight);
                }
                if (parent.Layout.Size.Y - Layout.Size.Y - padBottom > 0)
                {
                    newOffset.Y = MathHelper.Clamp(newOffset.Y, padTop, parent.Layout.Size.Y - Layout.Size.Y - padBottom);
                }
            }

            Location = newOffset;

            OnMouseDrag(new UiMouseArgs(mouse, this));
        }

        private void OnMouseDrag(UiMouseArgs arg)
        {
            if (MouseDrag != null)
            {
                MouseDrag(this, arg);
            }

            if (Parent != null && !arg.Handeled)
            {
                Parent.OnMouseDrag(arg);
            }
        }

        protected virtual void OnBoundsChanged()
        {
            if (BoundsChanged != null)
            {
                BoundsChanged(this, new UiEventArgs(this));
            }
        }
    }
}
